/******************************************************************************* * Copyright (c) 2014, 2017 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation ******************************************************************************/ package org.eclipse.ui.tests.concurrency; import java.lang.reflect.InvocationTargetException; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.core.tests.harness.TestBarrier; import org.eclipse.jface.dialogs.ProgressMonitorDialog; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.actions.WorkspaceModifyOperation; import org.eclipse.ui.progress.UIJob; import junit.framework.AssertionFailedError; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; /** * Regression test for bug 269121. The test verifies that deadlock described in * the bug is correctly resolved. Scenario: * <ol> * <li>UIJob job is created and a rule is set on the job using job.setRule(rule) * call.</li> * <li>WorkspaceModifyOperation operation is created with a rule conflicting * with the rule used for the job. * <li>The job is scheduled.</li> * <li>The operation is run in the UI thread by ProgressMonitorDialog using * dialog.run(false, true, operation) call</li>. * </ol> * Deadlock occurred because the operation run in the UI thread was waiting for * the rule already acquired by the job and asyncExec registered by the job * never got a chance to run in the UI thread, thus preventing the job from * ending and releasing the rule. * <p> * The solution is to make sure that operations executed in the UI thread spin * the event loop to process pending asyncExecs. * </p> */ public class TestBug269121 extends TestCase { public static Test suite() { return new TestSuite(TestBug269121.class); } public void testBug() throws InterruptedException, InvocationTargetException { Job job = new UIJob("UI job") { @Override public IStatus runInUIThread(IProgressMonitor monitor) { return Status.OK_STATUS; }; }; job.setRule(ResourcesPlugin.getWorkspace().getRoot()); final int[] status = new int[] { -1 }; WorkspaceModifyOperation operation = new WorkspaceModifyOperation() { @Override protected void execute(IProgressMonitor monitor) { status[0] = TestBarrier.STATUS_DONE; } }; final ProgressMonitorDialog dialog = new ProgressMonitorDialog( new Shell()); Job statusJob = new Job("Checking for deadlock") { @Override protected IStatus run(IProgressMonitor monitor) { try { TestBarrier.waitForStatus(status, TestBarrier.STATUS_DONE); return Status.OK_STATUS; } catch (AssertionFailedError e) { // syncExecs are processed by // UILockListener.aboutToWait(Thread) without running the // event loop so we can cancel the dialog to stop the test dialog.getShell().getDisplay().syncExec(() -> dialog.getProgressMonitor().setCanceled(true)); return Status.CANCEL_STATUS; } } }; job.schedule(); statusJob.schedule(); try { dialog.run(false, true, operation); } catch (InterruptedException e) { // expected if operation was cancelled } statusJob.join(); // run the event loop until the UI job is finished while (job.getResult() == null) { Display.getCurrent().readAndDispatch(); } job.join(); assertTrue("Deadlock occurred", statusJob.getResult().isOK()); } }